package cn.yunyichina.scheduler.jobs.service.refund.impl;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cn.yunyichina.provider.framework.dubbo.service.*;
import cn.yunyichina.utils.log.Log;
import cn.yunyichina.utils.log.constant.LogParamType;
import cn.yunyichina.utils.log.constant.LogScene;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yy.easyhealth.invoke.OutsideInvokeService;
import com.yy.easyhealth.invoke.dto.Request;

import cn.yunyichina.provider.framework.data.SpringContextHandler;
import cn.yunyichina.provider.iface.entity.base.OrderExceptionVo;
import cn.yunyichina.provider.iface.entity.base.OrderExtVo;
import cn.yunyichina.provider.iface.entity.clinic.ClinicOrder;
import cn.yunyichina.provider.iface.entity.common.ResCommon;
import cn.yunyichina.provider.iface.entity.common.Response;
import cn.yunyichina.provider.iface.entity.hisiface.clinic.RefundClinicInsuranceVo;
import cn.yunyichina.provider.iface.entity.pay.PaymentJournalVo;
import cn.yunyichina.scheduler.jobs.constant.BizConstant;
import cn.yunyichina.scheduler.jobs.service.refund.RefundHandlerService;
import cn.yunyichina.utils.commons.DateUtils;
import cn.yunyichina.utils.convert.JsonUtils;
import cn.yunyichina.utils.convert.ObjectParser;

@Service
public class RefundHandlerServiceImpl implements RefundHandlerService {

    private static final Logger logger = LoggerFactory.getLogger(RefundHandlerServiceImpl.class);

    @Override
    public void doExecute() {
        Response response = BaseService.callUnderlineFromInternal("yyt.base.order.exception.untreated.list", "");
        Log.i(LogScene.EXCEPTION_ORDER_REFUND,"执行了yyt.base.order.exception.untreated.list接口");
        if (!"0".equals(response.getResultCode())) {
            Log.i(LogScene.EXCEPTION_ORDER_REFUND,"doExecute, 查询异常订单失败");
            return;
        }

        List<OrderExceptionVo> orderExceptions = JsonUtils.parseArray(response.getResult(), OrderExceptionVo.class);
        if (CollectionUtils.isEmpty(orderExceptions)) {
            Log.i(LogScene.EXCEPTION_ORDER_REFUND,"不存在需要处理的异常订单");
            return;
        } else {
            for (OrderExceptionVo orderException : orderExceptions) {
                Map<String, String> dataMap = new HashMap<String, String>();
                dataMap.put("order_no", orderException.getOrderNo());
                // 在for循环中查询订单信息，后期需改造，应该跟异常订单信息一起取出。
                Log.i(LogScene.EXCEPTION_ORDER_REFUND,"yyt.base.order.by.order.no.out.get", LogParamType.REQUEST,orderException.getOrderNo(),JSON.toJSONString(dataMap));
                Response ordResponse = BaseService.callUnderlineFromInternal("yyt.base.order.by.order.no.out.get",
                        JsonUtils.toJsonString(dataMap));
                Log.i(LogScene.EXCEPTION_ORDER_REFUND,"yyt.base.order.by.order.no.out.get", LogParamType.REQUEST,orderException.getOrderNo(),JSON.toJSONString(ordResponse));
                if (!"0".equals(ordResponse.getResultCode())) {
                    Log.i(LogScene.EXCEPTION_ORDER_REFUND,"查询订单失败");
                    continue;
                }

                OrderExtVo order = JsonUtils.parseObject(ordResponse.getResult(), OrderExtVo.class);

                if (OrderExtVo.ORDER_PAY_STATUS_WAIT_REFUND.intValue() == order.getPayStatus().intValue()
                        || OrderExtVo.ORDER_PAY_STATUS_PART_REFUND.intValue() == order.getPayStatus().intValue()) {
                    // 待退费或部分退费成功才进行处理
                    int handleCount = 0;
                    if (orderException.getRefundHandlerCount() != null) {
                        handleCount = orderException.getRefundHandlerCount();
                    }
                    // 处理次数加1
                    handleCount++;

                    if (handleCount > BizConstant.MAX_REFUND_HANDLE_TIMES) {
                        Log.i(LogScene.EXCEPTION_ORDER_REFUND,orderException.getOrderNo(),"订单退费超过最大处理次数");
                        orderException.setHisQueryState((short) 2);
                        // 已达到处理最大次数，如果状态不是处理成功，则改为处理失败
                        if (orderException.getSelfPayHandlerFlag().intValue() != 1) {
                            orderException.setSelfPayHandlerFlag((short) 2);
                        }
                        if (orderException.getInsuranceHandlerFlag().intValue() != 1) {
                            orderException.setInsuranceHandlerFlag((short) 2);
                        }
                        updateOrderException(orderException);
                        continue;
                    } else {
                        orderException.setRefundHandlerCount(handleCount);
                        updateOrderException(orderException);
                    }

                    // 判断医保是否已处理成功
                    if (orderException.getInsuranceHandlerFlag().intValue() != 1) {
                        // 判断是否自费
                        if (order.getInsuranceMode() != null) {
                            // 判断是否医院结算接口
                            if (4 == order.getInsuranceMode().intValue() && order.getOrderType() == OrderExtVo.ORDER_TYPE_CLINIC) {
                                // 门诊订单
                                RefundClinicInsuranceVo refundClinicInsurance = new RefundClinicInsuranceVo();
                                refundClinicInsurance.setBranchCode(order.getBranchCode());
                                refundClinicInsurance.setHospitalCode(order.getOrgCode());
                                refundClinicInsurance.setDoctorCode(order.getDoctorCode());
                                refundClinicInsurance.setDeptCode(order.getDeptCode());
                                refundClinicInsurance.setPatCardNo(order.getMedicalCardNo());
                                refundClinicInsurance.setPatCardType(ObjectParser.toString(order.getMedicalCardType()));
                                refundClinicInsurance.setYytOrdNum(order.getOrderNo());

                                JSONObject extraParamsMap = JsonUtils.parseObject(order.getExtraParams());
                                refundClinicInsurance.setCancelBillNo((String) extraParamsMap.get("cancel_bill_no"));
                                refundClinicInsurance
                                        .setCancelSerialNo((String) extraParamsMap.get("cancel_serial_no"));
                                refundClinicInsurance.setMzBillId((String) extraParamsMap.get("mz_bill_id"));
                                refundClinicInsurance.setMzFeeId((String) extraParamsMap.get("mz_fee_id"));
                                refundClinicInsurance.setSsBillNo((String) extraParamsMap.get("ss_bill_no"));
                                refundClinicInsurance.setSsFeeNo((String) extraParamsMap.get("ss_fee_no"));

                                boolean isException = handleRefundClinicInsurance(refundClinicInsurance,
                                        orderException);
                                
                                Map<String, String> tmpMap = JsonUtils.parseObject(order.getExtraParams(), HashMap.class);
                                String isSetInsuranceException = tmpMap.get("is_set_insurance_exception");
                                if(StringUtils.isNotBlank(isSetInsuranceException)) {
                                    // 如果写单时医保结算失败，不管退费是否成功都需要入负流水
                                    addPaymentJournal(order, true);
                                } else if (!isException) {
                                    // 如果写单时医保结算成功，只有退费成功才需要入负流水
                                    addPaymentJournal(order, true);
                                }
                            } else if (4 == order.getInsuranceMode().intValue() && order.getOrderType() == OrderExtVo.ORDER_TYPE_REGISTER) {
                                //医院结算接口 挂号订单
                                Response hospitalResp =  RegisterService.callUnderlineFromInternal("yyt.register.reg.insurance.refund","{\"order_no\":\""+order.getOrderNo() +"\"}");
                                if ("0".equals(hospitalResp.getResultCode())) {
                                    //只有医保退费成功才需要入负流水,退款流水改在医保退款接口中插入
                                    addPaymentJournal(order, true);
                                    orderException.setHisQueryState((short) 2);
                                    orderException.setHandlerResult((short) 2);
                                    orderException.setInsuranceHandlerFlag((short) 1);
                                    orderException.setSelfPayHandlerFlag((short) 0);
                                    updateOrderException(orderException);
                                }

                            }else if (1 == order.getInsuranceMode().intValue()||2 == order.getInsuranceMode().intValue()) {
                                // 新平台医保支付
                                refundGeneral(order, orderException, true);
                            }
                        } else {
                            // 自费单不需要医保退费，直接把医保退费标示改成成功
                            Log.i(LogScene.EXCEPTION_ORDER_REFUND,orderException.getOrderNo(),"自费单直接把医保退费标示改成成功");
                            orderException.setHisQueryState((short) 2);
                            orderException.setHandlerResult((short) 2);
                            orderException.setInsuranceHandlerFlag((short) 1);
                            updateOrderException(orderException);
                        }
                    }

                    // 医保并且InsuranceMode=1时，医保退费已经退了自费部分，所以不需要做自费退费
                    if (order.getInsuranceMode() != null && 1 == order.getInsuranceMode().intValue()) {
                        // 如果医保退成功，则自费部分也退成功
                        if (orderException.getInsuranceHandlerFlag().intValue() == 1
                                && orderException.getSelfPayHandlerFlag().intValue() != 1) {
                            Log.i(LogScene.EXCEPTION_ORDER_REFUND,orderException.getOrderNo(),"doExecute, flag=" + orderException.getInsuranceHandlerFlag().intValue());
                            orderException.setHisQueryState((short) 2);
                            orderException.setHandlerResult((short) 2);
                            orderException.setSelfPayHandlerFlag((short) 1);
                            updateOrderException(orderException);
                        }
                    } else {
                        // 判断自费是否已处理成功
                        if (orderException.getSelfPayHandlerFlag().intValue() != 1) {
                            // 全额医保处理
                            if (order.getPayAmount() == null || 0 == order.getPayAmount().intValue()) {
                                Log.i(LogScene.EXCEPTION_ORDER_REFUND,orderException.getOrderNo(),"doExecute, 全额医保直接把自费退费标示改成成功。");
                                orderException.setHisQueryState((short) 2);
                                orderException.setHandlerResult((short) 2);
                                orderException.setSelfPayHandlerFlag((short) 1);
                                updateOrderException(orderException);
                            } else {
                                refundGeneral(order, orderException, false);
                            }
                        }
                    }

                    Log.i(LogScene.EXCEPTION_ORDER_REFUND,orderException.getOrderNo(),"doExecute, orderException.getSelfPayHandlerFlag()="
                            + orderException.getSelfPayHandlerFlag() + ", orderException.getInsuranceHandlerFlag()="
                            + orderException.getInsuranceHandlerFlag());
                    if (orderException.getSelfPayHandlerFlag().intValue() == 1
                            && orderException.getInsuranceHandlerFlag().intValue() == 1) {
                        // 医保和自费都处理成功后，把订单状态改成已退费
                        ClinicOrder co = new ClinicOrder();
                        co.setOrderNo(order.getOrderNo());
                        co.setOrgId(order.getOrgId());
                        // 已退费 
                        co.setPayStatus(7);
                        // 取消
                        co.setOrderStatus(7);
                        Log.i(LogScene.EXCEPTION_ORDER_REFUND,orderException.getOrderNo(),"doExecute, updateClinicOrder request=" + JSON.toJSONString(co));
                        ClinicService.getClinicClient().updateClinicOrder(co);

                        orderException.setHisQueryState((short) 2);
                        orderException.setHandlerResult((short) 3);
                        updateOrderException(orderException);
                    }
                }
            }
        }
    }

    private boolean handleRefundClinicInsurance(RefundClinicInsuranceVo refundClinicInsurance,
            OrderExceptionVo orderException) {
        boolean isException = false;
        try {
            Log.i(LogScene.EXCEPTION_ORDER_REFUND,"handleRefundClinicInsurance, refundClinicInsurance="
                    + JsonUtils.toJsonString(refundClinicInsurance));
            ResCommon<Void> voids = HisifaceService.getHisifaceClient().refundClinicInsurance(refundClinicInsurance);
            Log.i(LogScene.EXCEPTION_ORDER_REFUND,"handleRefundClinicInsurance, refundClinicInsurance=" + JSON.toJSONString(voids));
            if ("0".equals(voids.getResultCode())) {
                orderException.setHisQueryState((short) 2);
                orderException.setHandlerResult((short) 2);
                orderException.setInsuranceHandlerFlag((short) 1);
                orderException.setSelfPayHandlerFlag((short) 0);
                updateOrderException(orderException);
            } else {
                isException = true;
            }
        } catch (Exception e) {
            Log.e(LogScene.EXCEPTION_ORDER_REFUND,"refundClinicInsurance error", e);
            isException = true;
        }
        return isException;
    }

    private void updateOrderException(OrderExceptionVo orderException) {
        orderException.setUpdateTime(DateUtils.dateToString(new Date()));
        Log.i(LogScene.EXCEPTION_ORDER_REFUND,orderException.getOrderNo(),"updateOrderException, orderException=" + JSON.toJSONString(orderException));
        CacheService.setGlobalKey("clinic", "clinic_pay_exception_order", orderException.getOrderNo(),
                JSON.toJSONString(orderException), 0);

        BaseService.callUnderlineFromInternal("yyt.base.order.exception.update",
                JsonUtils.toJsonString(orderException));
    }

    private void addPaymentJournal(OrderExtVo order, boolean isInsurance) {
        Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"addPaymentJournal, isInsurance=" + isInsurance + ", order=" + JSON.toJSONString(order));
        PaymentJournalVo tmp = new PaymentJournalVo();
        tmp.setOrderNo(order.getOrderNo());
        tmp.setPayStatus((short) 2);
        Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"addPaymentJournal, paymentJournal=" + JSON.toJSONString(tmp));
        Response findPaymentJournalResponse = PayService.callUnderlineFromInternal("yyt.pay.payment.journal.find",
                JsonUtils.toJsonString(tmp));
        Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"addPaymentJournal, findPaymentJournalResponse=" + JSON.toJSONString(findPaymentJournalResponse));
        if ("0".equals(findPaymentJournalResponse.getResultCode())) {
            List<PaymentJournalVo> list = JsonUtils.parseArray(findPaymentJournalResponse.getResult(), PaymentJournalVo.class);
            int totalAmount = 0;
            for (PaymentJournalVo paymentJournalVo : list) {
                totalAmount += paymentJournalVo.getTotalAmount().intValue();
            }
            if(order.getTotalAmount().intValue() <= totalAmount) {
                Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"addPaymentJournal, 退费流水已存在，不再添加。");
                return;
            }
        }
         
        PaymentJournalVo paymentJournal=new PaymentJournalVo();
        paymentJournal.setOrgId(order.getOrgId());
        paymentJournal.setOrgName(order.getOrgName());
        paymentJournal.setOrgCode(order.getOrgCode());
        paymentJournal.setBranchId(order.getBranchId());
        paymentJournal.setBranchName(order.getBranchName());
        paymentJournal.setOrderNo(order.getOrderNo());
        paymentJournal.setOutOrderNo(order.getOutOrderNo());
        paymentJournal.setPaymentDealId(order.getPaymentDealId());
        if (isInsurance) {
        paymentJournal.setMedicareAmount(order.getMedicareAmount());
        paymentJournal.setAccountAmount(order.getAccountAmount());
        paymentJournal.setInsuranceAmount(order.getInsuranceAmount());
            paymentJournal.setTotalAmount(order.getInsuranceAmount());
            paymentJournal.setPayAmount(0);
        } else {
            paymentJournal.setMedicareAmount(0);
            paymentJournal.setAccountAmount(0);
            paymentJournal.setInsuranceAmount(0);
            paymentJournal.setPayAmount(order.getPayAmount());
            paymentJournal.setTotalAmount(order.getPayAmount());
        }
        
        paymentJournal.setPlatformId(order.getPlatformId());
        paymentJournal.setPlatformCode(order.getPlatformCode());
        paymentJournal.setPlatformName(order.getPlatformName());
        paymentJournal.setPlatformOrgAppName(order.getPlatformOrgAppName());
        paymentJournal.setPlatformOrgAppCode(order.getPlatformOrgAppCode());
        paymentJournal.setPayAppId(order.getPayAppId());
        paymentJournal.setDealAccount("");
        paymentJournal.setAccountName("");
        paymentJournal.setPayStatus((short) 2);
        paymentJournal.setHisOrderNo(order.getHisOrderNo());
        paymentJournal.setOrderType(order.getOrderType());
        paymentJournal.setCheckStatus((short)0);
        paymentJournal.setPayTime(DateUtils.dateToString(new Date()));
        
        if (order.getPayMode().intValue() == 99) {
            paymentJournal.setPayMode((short) 23);
        } else if (order.getPayMode().intValue() == 99) {
            paymentJournal.setPayMode((short) 13);
        } else {
            paymentJournal.setPayMode((short) order.getPayMode().intValue());
        }
        
        paymentJournal.setPlatformOrgAppCode(order.getPlatformOrgAppCode());
        paymentJournal.setBranchId(order.getBranchId());
        paymentJournal.setBranchName(order.getBranchName());
        paymentJournal.setSettlementStatus((short) 0);
        if (order.getInsuranceMode() != null) {
            paymentJournal.setInsuranceMode((short) order.getInsuranceMode().intValue());
        }
        paymentJournal.setBranchId(order.getBranchId());
        paymentJournal.setBranchName(order.getBranchName());
        paymentJournal.setBranchCode(order.getBranchCode());

        Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"新增支付流水请求参数->" + JSON.toJSONString(paymentJournal));
        Response addPaymentJournalResponse = PayService.callUnderlineFromInternal("yyt.pay.payment.journal.add",
                JsonUtils.toJsonString(paymentJournal));
        Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"新增支付流水返回参数->" + JSON.toJSONString(addPaymentJournalResponse));
    }

    private void refundGeneral(OrderExtVo order, OrderExceptionVo orderException, boolean isInsurance) {
        String regEx = "^[A-Za-z][0-9]{0,20}$";
        Pattern pattern = Pattern.compile(regEx);
        Matcher matcher = pattern.matcher(order.getOrderNo());
        boolean isMatch = matcher.matches();
        // 字符串是否与正则表达式相匹配
        Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"定时任务退费处理, 订单号" + order.getOrderNo() + ", 是否与旧平台订单规则匹配："+isMatch);
        // false为新平台订单
        if (!isMatch) {
            String method = "yyt.pay.refund.channel.origin";
            Map<String, Object> jsonMap = new HashMap<String, Object>();
            jsonMap.put("order_no", order.getOrderNo());
            jsonMap.put("his_order_no", StringUtils.isNotBlank(order.getHisOrderNo()) ? order.getHisOrderNo() : "1");
            jsonMap.put("his_serial_no", "");
            jsonMap.put("refund_amount", order.getTotalRealAmount());
            jsonMap.put("refund_time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            jsonMap.put("refund_reason", "退费调度正常退费");
            jsonMap.put("refund_flag", "1");
            jsonMap.put("is_from_refund_scheduler", "1");
            jsonMap.put("push_type", "1");
            Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"请求退款新系统参数->" + JSON.toJSONString(jsonMap));
            Response payResponse = PayService.callHumpFromInternal(method, JSON.toJSONString(jsonMap));
            Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"返回退款新系统参数->" + JSON.toJSONString(payResponse));
            boolean isSuccess = true;
            if ("0".equals(payResponse.getResultCode())) {
            	//更新该订单号的对应的自动退款标记为1
            	try{
                    Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"refundGeneral, 原渠道退款成功! OrderNo="+order.getOrderNo()+"开始自动更新该订单号的流水为自动退款!");
            		 PayService.getPayClient().updatePaymentJournalRefundFlagByOrderNo(order.getOrderNo(), (short)1);
                    Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"refundGeneral, 原渠道退款成功! OrderNo="+order.getOrderNo()+"更新该订单号的流水为自动退款成功!");
            	}catch(Exception ex){
                    Log.e(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"refundGeneral, 原渠道退款成功! 更新自动退款标记异常",ex);
                }
            	
                Map<String, String> dataMap = JSON.parseObject(payResponse.getResult(), Map.class);
                Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"refundGeneral, 原渠道退款成功! refund_yyt_deal_id=" + dataMap.get("refund_yyt_deal_id")
                        + ", payment_deal_id=" + dataMap.get("payment_deal_id"));
            } else if ("3010".equals(payResponse.getResultCode())) {
                Map<String, String> dataMap = JSON.parseObject(payResponse.getResult(), Map.class);
                Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"refundGeneral, 原渠道退款成功! refund_yyt_deal_id=" + dataMap.get("refund_yyt_deal_id")
                        + ", payment_deal_id=" + dataMap.get("payment_deal_id"));
            } else {
                isSuccess = false;
                Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"refundGeneral, 原渠道退款失败!");
            }
            if(isSuccess) {
                if(!order.getOrderNo().startsWith("YCA")) {
                    addPaymentJournal(order, isInsurance);
                }

                orderException.setHisQueryState((short) 2);
                orderException.setHandlerResult((short) 2);
                if(isInsurance) {
                    orderException.setInsuranceHandlerFlag((short) 1);
                } else {
                    orderException.setSelfPayHandlerFlag((short) 1);
                }
                updateOrderException(orderException);
            }
        } else {
            // 旧平台订单调回旧平台原渠道退费，不过此逻辑目前没有用。
            Request r = new Request();
            r.setMethodCode("refundGeneral");
            r.setResponseType("1");
            r.setParams("<appId>" + order.getPayAppId()
                    + "</appId><tradeMode>1</tradeMode><refundType>2</refundType><hisNewOrdNum>1</hisNewOrdNum>"
                    + "<psOrdNum>" + order.getOrderNo() + "</psOrdNum><refundAmout>"
                    + order.getTotalRealAmount() + "</refundAmout><refundTime>"
                    + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())
                    + "</refundTime><refundReason>正常退费</refundReason><partialOrAllrefund>1</partialOrAllrefund>"
                    + "<pushType>1</pushType>");

            try {
                OutsideInvokeService service = SpringContextHandler.getBean("outsideInvokeService");
                Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"refundGeneral, request=" + JSON.toJSONString(r));
                com.yy.easyhealth.invoke.dto.Response refundResponse = service.openService(r);
                Log.i(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"refundGeneral, response=" + JSON.toJSONString(refundResponse));
                if ("1".equals(refundResponse.getResultCode())) {
//                    addPaymentJournal(order, false);

                    orderException.setHisQueryState((short) 2);
                    orderException.setHandlerResult((short) 2);
                    orderException.setSelfPayHandlerFlag((short) 1);
                    updateOrderException(orderException);
                }
            } catch (Exception e) {
                Log.e(LogScene.EXCEPTION_ORDER_REFUND,order.getOrderNo(),"refundGeneral error", e);
            }
        }
    }

}
